#include <list>
#include "Session.h"
#include "application.h"
#include "Wheel.h"
#include "Camera.h"
#include "HeightMap.h"
#include "Player.h"
#include "NxPhysics.h"
#include "Object.h"
#include "InteractiveObject.h"
#include <OgreLight.h>
#include <OgreSceneNode.h>
#include <OgreManualObject.h>
#include <OgreRenderOperation.h>


#include <fstream>

using namespace Ogre;

Session::Session()
{
	initNx();
	mHeightmap=new HeightMap ();
    gSceneMgr->getRootSceneNode()->attachObject ((Ogre::MovableObject*) mHeightmap->getGeometry ());
	Wheel* player=new Wheel ();
	mObjects.push_back (player);

	mPlayer=new Player ();
	mPlayer->setTarget (player);
	//gCamera->setTarget(player);

	mLight = gSceneMgr->createLight("Light1");
	mLight->setType(Ogre::Light::LT_DIRECTIONAL);
	mLight->setCastShadows(1);
	gSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_MODULATIVE);
	gSceneMgr->setShadowColour(ColourValue(0.3,0.3,0.3));

	//mLight->setPosition(Ogre::Vector3(0, 150, 250));
	mLight->setDirection(Ogre::Vector3(-1,-1,0).normalisedCopy());
	mLight->setSpecularColour(.7,.7,1);
 
	gSceneMgr->setAmbientLight(Ogre::ColourValue(.3,.3,.3,1.));

	gSceneMgr->setSkyDome(true, "Environ_CloudySky", 5, 8);

}

Session::~Session()
{
	gSceneMgr->destroyLight(mLight);

	delete mHeightmap;
	delete mPlayer;

	releaseNx();
}

void updateDebugGeometry(float k)
{
	static Ogre::ManualObject* debugmo=NULL;
	static Ogre::SceneNode* debugno=NULL;

	
	if (!debugno)
	{
		debugno = gSceneMgr->getRootSceneNode()->createChildSceneNode("debug_geometry_node");
	}
	if (!debugmo)
	{
		debugmo = gSceneMgr->createManualObject("debug_geometry");
		debugno->attachObject(debugmo);
		debugmo->setRenderQueueGroup(70);
	}
	debugmo->clear();

	if (gSettings.mDebugGeometry)
	{
		const NxDebugRenderable *dbgRenderable=gScene->getDebugRenderable();
		const NxDebugRenderable& data = *dbgRenderable;

		debugmo->begin("wireframe_material", RenderOperation::OT_POINT_LIST);
		{
			NxU32 NbPoints = data.getNbPoints();
			const NxDebugPoint* Points = data.getPoints();
			while (NbPoints--)
			{
				debugmo->position(Points->p.x, Points->p.y, Points->p.z);
				Points++;
			}
		}
		debugmo->end();

		debugmo->begin("wireframe_material", RenderOperation::OT_LINE_LIST);
		{
			NxU32 NbLines = data.getNbLines();
			const NxDebugLine* Lines = data.getLines();
			while (NbLines--)
			{
				debugmo->position(Lines->p0.x, Lines->p0.y, Lines->p0.z);
				debugmo->position(Lines->p1.x, Lines->p1.y, Lines->p1.z);

				Lines++;
			}
		}
		debugmo->end();
		
		debugmo->begin("wireframe_material");
		{
			NxU32 NbTris = data.getNbTriangles();
			const NxDebugTriangle* Triangles = data.getTriangles();
			while (NbTris--)
			{
				debugmo->position(Triangles->p0.x, Triangles->p0.y, Triangles->p0.z);
				debugmo->position(Triangles->p1.x, Triangles->p1.y, Triangles->p1.z);
				debugmo->position(Triangles->p2.x, Triangles->p2.y, Triangles->p2.z);


				Triangles++;
			}
		}
		
		debugmo->end();
	}
}

void Session::update(float k)
{
	updatePhysics(k);
	//gCamera->update(k);
	gCamera->update(0.5*k, mPlayer->mTarget->mCameraPosition->_getDerivedPosition (), mPlayer->mTarget->mSightNode->_getDerivedPosition ());
	mPlayer->update(k);
	for (std::list<Object*>::iterator i=mObjects.begin();i != mObjects.end();i++)
		(*i)->update(k);

	updateDebugGeometry(k);
}

void Session::loadObjects(std::string filename)
{
	int i=0;

	char line[512],name[128];
	float x,y,z,s,yaw,pitch,roll;

	std::fstream f;

	f.open(filename.c_str(),std::fstream::in);
	InteractiveObject* o;

	for (;;)
	{
		f.getline(line,512);
		if (line[0] == 0) break;
		if (line[0] == '#') continue;
		sscanf(line,"%s %f %f %f %f %f %f %f",name,&x,&y,&z,&s,&yaw,&pitch,&roll);
		if (strcmp(name,"bacva.mesh") == 0)
			o=new Barrel();
		else if (strcmp(name,"Barrel.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"cempres1.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"cvijet.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"drvo_osnovno.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"drvo_trokutasto.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"drvo_VKMD.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"drvo_VKVD.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"gljiva1.mesh") == 0)
			o=new ObjectNarrow();
		else if (strcmp(name,"gljiva_velika.mesh") == 0)
			o=new ObjectNarrow();
		else
			o=new ObjectNarrow();



		o->create(name,Vector3(x,y,z),s,yaw,pitch,roll);
		o->init(Vector3(x,y,z));
		mObjects.push_back(o);
	}
	f.close();
}

void Session::initNx()
{
    //Create the physics SDK
    gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
    if (!gPhysicsSDK)  return;

	// Set the physics parameters
	gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);

	// Set the debug visualization parameters
	gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
	gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
	gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);

    // Create the scene
    NxSceneDesc sceneDesc;
    sceneDesc.gravity               = gDefaultGravity;

    gScene = gPhysicsSDK->createScene(sceneDesc);

/*
    NxSceneDesc sceneDesc;
 	sceneDesc.simType				= NX_SIMULATION_SW;
    sceneDesc.gravity               = gDefaultGravity;
    gScene = gPhysicsSDK->createScene(sceneDesc);	
	if(!gScene)
	{ 
		sceneDesc.simType			= NX_SIMULATION_SW; 
		gScene = gPhysicsSDK->createScene(sceneDesc);  
		if(!gScene) return;
	}
*/

	// Create the default material
	NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0); 
	defaultMaterial->setRestitution(0.2);
	defaultMaterial->setStaticFriction(200);
	defaultMaterial->setDynamicFriction(200);

	// Create the objects in the scene
	groundPlane  = createGroundPlane();

	// Get the current time
	//UpdateTime();

    // Start the first frame of the simulation
    if (gScene)  updatePhysics(0.01);

}

void Session::updatePhysics(float timeSinceLastFrame)
{
	// Start collision and dynamics for delta time since the last frame
    gScene->simulate(timeSinceLastFrame);
	gScene->flushStream();
	int i=0;
	while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false))
		i++;
}

void Session::releaseNx()
{
    if (gScene)
	{
		gScene->fetchResults(NX_RIGID_BODY_FINISHED, false);  // Make sure to fetchResults() before shutting down
		gPhysicsSDK->releaseScene(*gScene);
	}
	if (gPhysicsSDK)  gPhysicsSDK->release();
}

NxActor* Session::createGroundPlane()
{
	// Create a plane with default descriptor
	NxPlaneShapeDesc planeDesc;
	NxActorDesc actorDesc;
	planeDesc.materialIndex=0;
	actorDesc.shapes.pushBack(&planeDesc);
	return gScene->createActor(actorDesc);
}


